package org.tulkas.util;

import java.util.Calendar;
import java.util.Date;

/**
 * This class is used for converting an unknow type object to a specific type.
 * It will decide how to convert depends on the source and destination types. 
 * 
 * @author <a href="mailto:tulkas.web@gmail.com">Tulkas</a>
 *
 */
public abstract class Converter {

	/**
	 * Convert to a Number
	 * Date can be converted to a long as it's getTime();
	 * If the source is string, convert to double.
	 * 
	 * @param input
	 * @return
	 */
	public static Number toNumber(Object input) {
		if (input == null)
			return null;
		else if (input instanceof Number)
			return (Number) input;
		else if (input instanceof Date)
			return ((Date) input).getTime();
		else if (input instanceof Boolean)
			return (((Boolean) input).booleanValue()) ? 1 : 0;
		else {
			String str = input.toString();
			return Double.parseDouble(str);
		}
	}

	/**
	 * Convert to a int
	 * 
	 * @param input
	 * @return
	 */
	public static int toInt(Object input) {
		if (input == null)
			return 0;
		else if (input instanceof String)
			return Integer.parseInt((String) input);
		else {
			Number num = toNumber(input);
			return num.intValue();
		}
	}

	/**
	 * Convert to a long
	 * Date can be converted to a long as it's getTime();
	 * 
	 * @param input
	 * @return
	 */
	public static long toLong(Object input) {
		if (input == null)
			return 0;
		else if (input instanceof String)
			return Long.parseLong((String) input);
		else if (input instanceof Date) {
			return ((Date) input).getTime();
		} else if (input instanceof Calendar) {
			return ((Calendar) input).getTimeInMillis();
		} else {
			Number num = toNumber(input);
			return num.longValue();
		}
	}

	/**
	 * Convert to a short
	 * 
	 * @param input
	 * @return
	 */
	public static short toShort(Object input) {
		if (input == null)
			return 0;
		else if (input instanceof String)
			return Short.parseShort((String) input);
		else {
			Number num = toNumber(input);
			return num.shortValue();
		}
	}

	/**
	 * Conver to a byte
	 * 
	 * @param input
	 * @return
	 */
	public static byte toByte(Object input) {
		if (input == null)
			return 0;
		else if (input instanceof String)
			return Byte.parseByte((String) input);
		else {
			Number num = toNumber(input);
			return num.byteValue();
		}
	}

	/**
	 * Convert to a float
	 * 
	 * @param input
	 * @return
	 */
	public static float toFloat(Object input) {
		if (input == null)
			return 0;
		else if (input instanceof String)
			return Float.parseFloat((String) input);
		else {
			Number num = toNumber(input);
			return num.floatValue();
		}
	}

	/**
	 * Convert to a double
	 * 
	 * @param input
	 * @return
	 */
	public static double toDouble(Object input) {
		if (input == null)
			return 0;
		else if (input instanceof String)
			return Double.parseDouble((String) input);
		else {
			Number num = toNumber(input);
			return num.doubleValue();
		}
	}

	/**
	 * Convert to a boolean.
	 * For numbers, 0 represents false, other than 0 represents true.
	 * 
	 * @param input
	 * @return
	 */
	public static boolean toBoolean(Object input) {
		if (input == null)
			return false;
		else if (input instanceof Boolean)
			return (Boolean) input;
		else if (input instanceof String)
			return Boolean.parseBoolean((String) input);
		else if (input instanceof Number)
			return (((Number) input).intValue() == 0) ? false : true;
		else {
			return true;
		}
	}

	/**
	 * Convert to a char
	 * 
	 * @param input
	 * @return
	 */
	public static char toChar(Object input) {
		if (input == null)
			return 0;
		else if (input instanceof Character)
			return (Character) input;
		else if (input instanceof Number)
			return (char) ((Number) input).intValue();
		else {
			String str = input.toString();
			return (str.length() > 0) ? str.charAt(0) : 0;
		}
	}

	/**
	 * Convert to a Date object.
	 * Numbers can convert to a Date by taking it as the millisecond format.
	 * 
	 * @param input
	 * @return
	 */
	public static Date toDate(Object input) {
		if (input == null)
			return null;
		else if (input instanceof Date)
			return (Date) input;
		else if (input instanceof Calendar) {
			return ((Calendar) input).getTime();
		} else {
			long longValue = toLong(input);
			return new Date(longValue);
		}
	}

	/**
	 * Convert to a string.
	 * 
	 * @param input
	 * @return
	 */
	public static String toString(Object input) {
		if (input == null)
			return null;
		if (input instanceof String)
			return (String) input;
		else if (input instanceof char[])
			return new String((char[]) input);
		else if (input instanceof byte[])
			return new String((byte[]) input);
		return input.toString();
	}

	/**
	 * Convert to a byte array. for Strings ,will use the default charset.
	 * 
	 * @param input
	 * @return
	 */
	public static byte[] toBytes(Object input) {
		if (input == null)
			return null;
		else if (input instanceof byte[])
			return (byte[]) input;
		else {
			String str = input.toString();
			return str.getBytes();
		}
	}

	/**
	 * Convert to a char array.
	 * 
	 * @param input
	 * @return
	 */
	public static char[] toChars(Object input) {
		if (input == null)
			return null;
		else if (input instanceof char[])
			return (char[]) input;
		else {
			String str = input.toString();
			return str.toCharArray();
		}
	}

	public static Object toObject(Object input, Class<?> cls) {
		if (boolean.class.equals(cls) || Boolean.class.equals(cls)) {
			return toBoolean(input);
		} else if (byte.class.equals(cls) || Byte.class.equals(cls)) {
			return toByte(input);
		} else if (byte[].class.equals(cls)) {
			return toBytes(input);
		} else if (char.class.equals(cls) || Character.class.equals(cls)) {
			return toChar(input);
		} else if (char[].class.equals(cls)) {
			return toChars(input);
		} else if (Date.class.equals(cls)) {
			return toDate(input);
		} else if (double.class.equals(cls) || Double.class.equals(cls)) {
			return toDouble(input);
		} else if (float.class.equals(cls) || Float.class.equals(cls)) {
			return toFloat(input);
		} else if (int.class.equals(cls) || Integer.class.equals(cls)) {
			return toInt(input);
		} else if (long.class.equals(cls) || Long.class.equals(cls)) {
			return toLong(input);
		} else if (short.class.equals(cls) || Short.class.equals(cls)) {
			return toShort(input);
		} else if (String.class.equals(cls)) {
			return toString(input);
		} else {
			return input;
		}
	}
}
